﻿// Bing Maps ASP.NEt Control is a community project available under the Microsoft Public License (Ms-PL)
// Code is provided as is and with no warrenty – Use at your own risk
// View the project and the latest code at http://BingMapsASP.codeplex.com

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;
using Microsoft.Live.ServerControls.VE.Constants;

namespace Microsoft.Live.ServerControls.VE
{
    [Serializable]
    internal class ClientData
    {
        #region Properties
        public List<ShapeLayer> ShapeLayers { get; set; }
        public List<TileSourceSpecification> TileLayers { get; set; }

        public List<String> DeletedShapeLayers { get; set; }
        public List<String> DeletedTileLayers { get; set; }

        public bool Clear { get; set; }
        #endregion

        #region Constructor
        public ClientData()
        {
            ShapeLayers = new List<ShapeLayer>();
            //add base map layer
            ShapeLayers.Add(new ShapeLayer { BaseLayer = true});
            TileLayers = new List<TileSourceSpecification>();

            DeletedShapeLayers = new List<string>();
            DeletedTileLayers = new List<string>();
            Clear = false;
        }
        #endregion

        #region Methods

        /// <summary>
        /// Internal to trigger tracking
        /// </summary>
        private void SetDataLoaded()
        {
            foreach (ShapeLayer layer in ShapeLayers)
            {
                layer.DataLoaded = true;
            }
            foreach (TileSourceSpecification layer in TileLayers)
            {
                layer.DataLoaded = true;
            }
        }

        public void AddShape(Shape shape)
        {
            ShapeLayers[0].AddShape(shape);
        }

        public void AddShapeLayer(ShapeLayer layer)
        {
            if (layer.ID != null)
            {
                ResourceManager rm = new ResourceManager("Microsoft.Live.ServerControls.VE.Map", Assembly.GetExecutingAssembly());
                throw new System.Exception(rm.GetString(ExceptionProperty.ExceptionClientIDReadOnly));
            }
            layer.State = DataState.Added;
            ShapeLayers.Add(layer);
        }

        public void AddTileLayer(TileSourceSpecification layerSource)
        {
            layerSource.State = DataState.Added;
            TileLayers.Add(layerSource);
        }

        public void AddTileLayer(TileSourceSpecification layerSource, bool visible)
        {
            layerSource.State = DataState.Added;
            layerSource.Visible = visible;
            TileLayers.Add(layerSource);
        }

        public void ClearShapes()
        {
            Clear = true;
            ShapeLayers = new List<ShapeLayer>();
            //always base layer
            ShapeLayers.Add(new ShapeLayer { BaseLayer = true });
        }

        public void DeleteAllShapeLayers()
        {
            foreach (ShapeLayer layer in ShapeLayers)
            {
                if (layer.State != DataState.Added)
                {
                    DeletedShapeLayers.Add(layer.ID);
                }
            }
            ShapeLayers = new List<ShapeLayer>();
            //always base layer
            ShapeLayers.Add(new ShapeLayer { BaseLayer = true });
        }

        public void DeleteAllShapes()
        {
            foreach (ShapeLayer layer in ShapeLayers)
            {
                layer.DeleteAllShapes();
            }
        }

        public void DeleteShape(Shape shape)
        {
            foreach (ShapeLayer layer in ShapeLayers)
            {
                if (layer.Shapes.Contains(shape))
                {
                    layer.DeleteShape(shape);
                    break;
                }
            }
        }

        public void DeleteShapeLayer(string layerID)
        {
            foreach (ShapeLayer layer in ShapeLayers)
            {
                if (layer.ID == layerID)
                {
                    if (layer.State != DataState.Added && !layer.BaseLayer)
                    {
                        DeletedShapeLayers.Add(layerID);
                    }
                    ShapeLayers.Remove(layer);
                    break;
                }
            }
        }

        public void DeleteTileLayer(string layerID)
        {
            foreach (TileSourceSpecification layer in TileLayers)
            {
                if (layer.ID == layerID)
                {
                    if (layer.State != DataState.Added)
                    {
                        DeletedTileLayers.Add(layerID);
                    }
                    TileLayers.Remove(layer);
                    break;
                }
            }
        }

        public Shape GetShapeByID(string shapeID)
        {
            foreach (ShapeLayer layer in ShapeLayers)
            {
                Shape foundshape = layer.GetShapeByID(shapeID);
                if (foundshape != null)
                {
                    return foundshape;
                }
            }
            return null;
        }

        public ShapeLayer GetShapeLayerByIndex(int index)
        {
            if (index < ShapeLayers.Count)
            {
                return ShapeLayers[index];
            }
            return null;
        }

        public int GetShapeLayerCount()
        {
            return ShapeLayers.Count;
        }

        public TileSourceSpecification GetTileLayerByID(string layerID)
        {
            foreach (TileSourceSpecification layer in TileLayers)
            {
                if (layer.ID == layerID)
                {
                    return layer;
                }
            }
            return null;
        }

        public TileSourceSpecification GetTileLayerByIndex(int index)
        {
            if (index < TileLayers.Count)
            {
                return TileLayers[index];
            }
            return null;
        }

        public int GetTileLayerCount()
        {
            return TileLayers.Count;
        }

        public void HideAllShapeLayers()
        {
            foreach (ShapeLayer layer in ShapeLayers)
            {
                layer.Visible = false;
            }
        }

        public void HideTileLayer(string layerID)
        {
            GetTileLayerByID(layerID).Visible = false;
        }

        public void ShowAllShapeLayers()
        {
            foreach (ShapeLayer layer in ShapeLayers)
            {
                layer.Visible = true;
            }
        }

        public void ShowTileLayer(string layerID)
        {
            GetTileLayerByID(layerID).Visible = true;
        }

        public ShapeLayer GetShapeLayerByShape(Shape shape)
        {
            foreach (ShapeLayer layer in ShapeLayers)
            {
                if (layer.Contains(shape))
                {
                    return layer;
                }
            }
            return null;
        }

        public string GetDataJSON(bool allData)
        {
            string data;
            JavaScriptSerializer jss = new JavaScriptSerializer();

            //On full postback or when not persisting server side return all data to be recreated.
            if (allData)
            {
                //remove all deleted information, redundant
                DeletedShapeLayers = new List<string>();
                DeletedTileLayers = new List<string>();
                //Clear = false;
                //Serialize everything
                data = jss.Serialize(this);
            }else
            {
                //manually create JSON
                StringBuilder str = new StringBuilder();
                str.Append("{");
                //deleted records or full Clear
                if (Clear)
                {
                    str.Append("\"Clear\":");
                    str.Append(jss.Serialize(true));
                    str.Append(",");
                }
                else
                {
                    if (DeletedShapeLayers.Count > 0)
                    {
                        str.Append("\"DeletedShapeLayers\":");
                        str.Append(jss.Serialize(DeletedShapeLayers));
                        str.Append(",");
                    }

                    //Get all deleted shapes.
                    List<String> DeletedShapes  = new List<string>();
                    foreach (ShapeLayer layer in ShapeLayers)
                    {
                        DeletedShapes.AddRange(layer.DeletedShapes);
                    }
                    if (DeletedShapes.Count > 0)
                    {
                        str.Append("\"DeletedShapes\":");
                        str.Append(jss.Serialize(DeletedShapes));
                        str.Append(",");
                    }
                }

                if (DeletedTileLayers.Count > 0)
                {
                    str.Append("\"DeletedTileLayers\":");
                    str.Append(jss.Serialize(DeletedTileLayers));
                    str.Append(",");
                }

                List<ShapeLayer> newShapeLayers = new List<ShapeLayer>();

                bool insertSeperator = false;
                //only want new items and the set of changes to existing
                str.Append("\"ModifiedShapeLayers\":[");
                foreach (ShapeLayer layer in ShapeLayers)
                {
                    //leave added layers and shapes only
                    if (layer.State == DataState.Added)
                    {
                        newShapeLayers.Add(layer);
                    } else {
                        if (layer.State == DataState.Modified)
                        {
                            //write out changes
                            if (insertSeperator)
                            {
                                str.Append(",");
                            }
                            else
                            {
                                insertSeperator = true;
                            }
                            str.Append(layer.propertyChangestoJSON());
                        }
                     } 
                }
                str.Append("],");

                //write out layer additions
                if (newShapeLayers.Count > 0)
                {
                    str.Append("\"ShapeLayers\":");
                    str.Append(jss.Serialize(newShapeLayers));
                    str.Append(",");
                }

                List<TileSourceSpecification> newTileLayers = new List<TileSourceSpecification>();
                insertSeperator = false;
                str.Append("\"ModifiedTileLayers\":[");
                foreach (TileSourceSpecification layer in TileLayers)
                {
                    if (layer.State == DataState.Added)
                    {
                        newTileLayers.Add(layer);
                    }
                    if (layer.State == DataState.Modified)
                    {
                        //write out changes
                        if (insertSeperator)
                        {
                            str.Append(",");
                        }else
                        {
                            insertSeperator = true;
                        }
                        str.Append(layer.propertyChangestoJSON());
                    }
                }
                str.Append("],");

                //write out layer additions
                if (newTileLayers.Count > 0)
                {
                    str.Append("\"TileLayers\":");
                    str.Append(jss.Serialize(newTileLayers));
                    str.Append(",");
                }
 
                data = str.ToString().Substring(0, str.Length - 1) + "}";
            }

            return data;
        }

        public void SetDataFromJSON(string strJSON)
        {
            if (strJSON.Length > 0)
            {
                JavaScriptSerializer jss = new JavaScriptSerializer();
                ClientData temp = jss.Deserialize<ClientData>(strJSON);

                //any html inside shapes was encoded, need to decode
                //reset all State flags.
                bool BaseLayerSet = false;
                foreach (ShapeLayer layer in temp.ShapeLayers)
                {
                    if (!BaseLayerSet)
                    {
                        layer.BaseLayer = true;
                        BaseLayerSet = true;
                    }
                    foreach (Shape shape in layer.Shapes)
                    {
                        shape.Title = HttpUtility.HtmlDecode(shape.Title);
                        shape.Description = HttpUtility.HtmlDecode(shape.Description);
                        shape.State = DataState.Unchanged;
                    }
                    layer.State = DataState.Unchanged;
                }
                foreach (TileSourceSpecification layer in temp.TileLayers)
                {
                    layer.State = DataState.Unchanged;
                }
                //assign
                ShapeLayers = temp.ShapeLayers;
                TileLayers = temp.TileLayers;
                SetDataLoaded();
            }
        }

        #endregion
    }
}
